package com.it.mmx.memory.circuit.breaker.monitor;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 系统内存监控器（兼容Windows和Linux）
 * @author 刘仲
 */
public class SystemMemoryMonitor {

    private final AtomicReference<Double> remainingMemoryPercent = new AtomicReference<>(100.0);
    private final AtomicBoolean isMemoryCritical = new AtomicBoolean(false);
    private final double thresholdPercent; // 熔断阈值
    private static final int CHECK_INTERVAL_SECONDS = 1;
    private static final boolean IS_WINDOWS = System.getProperty("os.name").toLowerCase().contains("windows");

    public SystemMemoryMonitor(double thresholdPercent) {
        this.thresholdPercent = thresholdPercent;
        startMonitoring();
    }

    /**
     * 启动内存监控定时任务
     */
    private void startMonitoring() {
        ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(runnable -> {
            Thread thread = new Thread(runnable, "memory-monitor-thread");
            thread.setDaemon(true); // 守护线程，随应用退出
            return thread;
        });
        scheduler.scheduleAtFixedRate(this::updateMemoryStatus, 0, CHECK_INTERVAL_SECONDS, TimeUnit.SECONDS);
    }

    /**
     * 更新内存状态
     */
    private void updateMemoryStatus() {
        try {
            // 1. 优先检查容器环境内存
            long totalMemory = getContainerTotalMemory();
            long availableMemory = getContainerAvailableMemory();

            // 2. 非容器环境：根据系统类型获取内存
            if (totalMemory <= 0 || availableMemory <= 0) {
                if (IS_WINDOWS) {
                    MemoryInfo info = getWindowsMemoryInfo();
                    totalMemory = info.total;
                    availableMemory = info.available;
                } else {
                    MemoryInfo info = getLinuxMemoryInfo();
                    totalMemory = info.total;
                    availableMemory = info.available;
                }
            }

            // 3. 计算剩余内存百分比并判断是否触发熔断
            if (totalMemory > 0 && availableMemory >= 0) {
                double percent = (double) availableMemory / totalMemory * 100;
                percent = new BigDecimal(percent).setScale(2, RoundingMode.HALF_UP).doubleValue();
                remainingMemoryPercent.set(percent);
                isMemoryCritical.set(percent <= thresholdPercent);
            }
        } catch (Exception e) {
            // 监控异常不影响主流程
            // e.printStackTrace();
        }
    }

    /**
     * Windows系统内存信息获取
     */
    private MemoryInfo getWindowsMemoryInfo() {
        com.sun.management.OperatingSystemMXBean osBean =
                (com.sun.management.OperatingSystemMXBean) ManagementFactory.getPlatformMXBean(
                        com.sun.management.OperatingSystemMXBean.class);
        long total = osBean.getTotalPhysicalMemorySize();
        long available = osBean.getFreePhysicalMemorySize();
        return new MemoryInfo(total, available);
    }

    /**
     * Linux系统内存信息获取（通过/proc/meminfo）
     */
    private MemoryInfo getLinuxMemoryInfo() throws IOException {
        long total = 0;
        long available = 0;
        try (BufferedReader reader = new BufferedReader(new FileReader("/proc/meminfo"))) {
            String line;
            while ((line = reader.readLine()) != null) {
                if (line.startsWith("MemTotal:")) {
                    total = parseMemInfoValue(line) * 1024; // 转换为字节
                } else if (line.startsWith("MemAvailable:")) {
                    available = parseMemInfoValue(line) * 1024; // 转换为字节
                }
            }
        }
        return new MemoryInfo(total, available);
    }

    /**
     * 解析Linux内存信息数值
     */
    private long parseMemInfoValue(String line) {
        String[] parts = line.trim().split("\\s+");
        return Long.parseLong(parts[1]);
    }

    /**
     * 容器环境总内存获取
     */
    private long getContainerTotalMemory() {
        try (BufferedReader reader = new BufferedReader(
                new FileReader("/sys/fs/cgroup/memory/memory.limit_in_bytes"))) {
            String line = reader.readLine();
            if (line != null) {
                return Long.parseLong(line.trim());
            }
        } catch (IOException e) {
            // 非容器环境忽略
        }
        return -1;
    }

    /**
     * 容器环境可用内存获取
     */
    private long getContainerAvailableMemory() {
        try {
            long total = getContainerTotalMemory();
            if (total <= 0) return -1;

            try (BufferedReader reader = new BufferedReader(
                    new FileReader("/sys/fs/cgroup/memory/memory.usage_in_bytes"))) {
                String line = reader.readLine();
                if (line != null) {
                    long used = Long.parseLong(line.trim());
                    return total - used;
                }
            }
        } catch (IOException e) {
            // 非容器环境忽略
        }
        return -1;
    }

    /**
     * 内存信息封装类
     */
    private static class MemoryInfo {
        long total;
        long available;

        MemoryInfo(long total, long available) {
            this.total = total;
            this.available = available;
        }
    }

    // 对外提供的监控方法
    public double getRemainingMemoryPercent() {
        return remainingMemoryPercent.get();
    }

    public boolean isNeedCircuitBreak() {
        return isMemoryCritical.get();
    }
}
